package com.example.demo.config;

import java.lang.reflect.Method;
import java.time.Duration;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import javax.annotation.Resource;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties.Jedis;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.cache.interceptor.SimpleKeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.util.Assert;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;

import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPoolConfig;

@Configuration
@EnableCaching // 启用缓存的意思
@Conditional(RedisCondition.class)//自定义的condition的match方法返回值为true时，才会进入该方法创建bean
public class RedisConfig extends CachingConfigurerSupport{
	
	/**
	 * json序列化
	 * 
	 * @return
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Bean
	public RedisSerializer<Object> jackson2JsonRedisSerializer() {
		// 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
		Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);

		ObjectMapper mapper = new ObjectMapper();
		mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
		mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
		serializer.setObjectMapper(mapper);
		return serializer;
	}
	 /**
     * redis模板配置
     * @param redisConnectionFactory
     * @return
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
     // set key serializer
 		StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
 		redisTemplate.setKeySerializer(stringRedisSerializer);
 		redisTemplate.setHashKeySerializer(stringRedisSerializer);

 		// set value serializer
 		redisTemplate.setDefaultSerializer(jackson2JsonRedisSerializer());
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

	/**
	 * 在使用@Cacheable时，如果不指定key，则使用找个默认的key生成器生成的key
	 *
	 */
	@Override
	@Bean
	public KeyGenerator keyGenerator() {
		return new SimpleKeyGenerator() {
			@Override
			public Object generate(Object target, Method method, Object... params) {
				StringBuilder sb = new StringBuilder();
				sb.append(target.getClass().getName());
				sb.append(".").append(method.getName());

				StringBuilder paramsSb = new StringBuilder();
				for (Object param : params) {
					// 如果不指定，默认生成包含到键值中
					if (param != null) {
						sb.append(JSONObject.toJSON(param));
						sb.append("-");
					}
				}
				if (paramsSb.length() > 0) {
					sb.append("_").append(paramsSb);
				}
				return method.getName() + ":" + sb.toString().hashCode();
			}

		};

	}


	 @Autowired
	 private RedisTemplate<String, String> redisTemplate;
	  
    @Override
    @Bean
    public CacheManager cacheManager() {
	
        RedisConnectionFactory connectionFactory = redisTemplate.getConnectionFactory();
		//上面实现的缓存读写
        RedisCacheWriterCustomer cachaWriterCustomer 
        = new RedisCacheWriterCustomer(connectionFactory);

        return new RedisCacheManager(cachaWriterCustomer,redisCacheConfiguration());
    }
    
    @Bean
    public RedisCacheConfiguration redisCacheConfiguration(){

        RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig();

        configuration = configuration.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())).entryTtl(Duration.ZERO);

        return configuration;
    }
    
	 /**
     * 获取redis的集群的配置ip+端口
     */
	@Resource
    private  RedisProperties redisProperties;
    
    
    @Bean
    public JedisCluster redisCluster() {
    	if(redisProperties.getCluster()==null)
    		return null;
        Set<HostAndPort> nodes = new HashSet<>();
        for (String node : redisProperties.getCluster().getNodes()) {
            String[] parts = StringUtils.split(node, ":");
            Assert.state(parts.length == 2, "redis node shoule be defined as 'host:port', not '" + Arrays.toString(parts) + "'");
            nodes.add(new HostAndPort(parts[0], Integer.valueOf(parts[1])));
        }
        //没有配置连接池使用默认的连接池
        //return new JedisCluster(nodes);
 
        //创建集群对象
        JedisCluster jedisCluster = null;
        if (!StringUtils.isBlank(redisProperties.getPassword())) {
            jedisCluster = new JedisCluster(nodes, 5000, 1500, 3, redisProperties.getPassword(), jedisPoolConfig());
        } else {
            jedisCluster = new JedisCluster(nodes,5000,jedisPoolConfig());
        }
        return jedisCluster;
    }
 
    /**
     * jedis的连接池
     * @return
     */
    public JedisPoolConfig jedisPoolConfig(){
        JedisPoolConfig jedisPoolConfig =new JedisPoolConfig();
        Jedis jedis=redisProperties.getJedis();
        jedisPoolConfig.setMinIdle(jedis.getPool().getMinIdle());
        jedisPoolConfig.setMaxIdle(jedis.getPool().getMaxIdle());
        jedisPoolConfig.setMaxWaitMillis(24000);
        return jedisPoolConfig;
    }


}
